use serde::{Deserialize, Serialize};

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ConnectOptions {
    pub local_path: String,
    pub url: Option<String>,
    pub auth_token: Option<String>,
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ConnectionId(pub String);

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ExecuteOptions {
    pub connection_id: String,
    pub sql: String,
    pub params: Option<Vec<Value>>,
    pub named_params: Option<std::collections::HashMap<String, Value>>,
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct QueryOptions {
    pub connection_id: String,
    pub sql: String,
    pub params: Option<Vec<Value>>,
    pub named_params: Option<std::collections::HashMap<String, Value>>,
}

// Vector-specific query options for nearest neighbor search
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct VectorTopKOptions {
    pub connection_id: String,
    pub index_name: String,
    pub query_vector: Vec<f32>, // JSON array representation
    pub k: u32,
    pub vector_type: Option<VectorType>, // defaults to Float32 if not specified
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SyncOptions {
    pub connection_id: String,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CloseOptions {
    pub connection_id: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct QueryResult {
    pub columns: Vec<String>,
    pub rows: Vec<Vec<Value>>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ExecuteResult {
    pub rows_affected: u64,
    pub last_insert_rowid: Option<i64>,
}

// Vector types supported by libSQL
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum VectorType {
    Float64,   // F64_BLOB
    Float32,   // F32_BLOB (most common)
    Float16,   // F16_BLOB
    FloatB16,  // FB16_BLOB (bfloat16)
    Float8,    // F8_BLOB
    Float1Bit, // F1BIT_BLOB
}

impl VectorType {
    pub fn as_function_name(&self) -> &'static str {
        match self {
            VectorType::Float64 => "vector64",
            VectorType::Float32 => "vector32",
            VectorType::Float16 => "vector16",
            VectorType::FloatB16 => "vectorb16",
            VectorType::Float8 => "vector8",
            VectorType::Float1Bit => "vector1bit",
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type")]
pub enum Value {
    Null,
    Integer(i64),
    Real(f64),
    Text(String),
    Blob(Vec<u8>),
    // Vector support - stored as JSON array for easy serialization
    Vector {
        data: Vec<f32>,
        vector_type: VectorType,
    },
}

impl From<libsql::Value> for Value {
    fn from(value: libsql::Value) -> Self {
        match value {
            libsql::Value::Null => Value::Null,
            libsql::Value::Integer(v) => Value::Integer(v),
            libsql::Value::Real(v) => Value::Real(v),
            libsql::Value::Text(v) => Value::Text(v),
            libsql::Value::Blob(v) => Value::Blob(v),
        }
    }
}

impl TryFrom<Value> for libsql::Value {
    type Error = crate::Error;

    fn try_from(value: Value) -> Result<Self, Self::Error> {
        match value {
            Value::Null => Ok(libsql::Value::Null),
            Value::Integer(v) => Ok(libsql::Value::Integer(v)),
            Value::Real(v) => Ok(libsql::Value::Real(v)),
            Value::Text(v) => Ok(libsql::Value::Text(v)),
            Value::Blob(v) => Ok(libsql::Value::Blob(v)),
            Value::Vector {
                data,
                vector_type: _,
            } => {
                // Convert vector to JSON string representation for libSQL
                let json_array = serde_json::to_string(&data).map_err(|_| {
                    crate::Error::InvalidParameter(0, "Failed to serialize vector data".to_string())
                })?;
                Ok(libsql::Value::Text(json_array))
            }
        }
    }
}

pub trait ToLibsqlParam {
    fn to_libsql_value(&self) -> Result<libsql::Value, crate::Error>;
}

impl ToLibsqlParam for Value {
    fn to_libsql_value(&self) -> Result<libsql::Value, crate::Error> {
        self.clone().try_into()
    }
}

pub fn convert_positional_params(
    params: Option<Vec<Value>>,
) -> Result<Vec<libsql::Value>, crate::Error> {
    match params {
        Some(values) => values
            .into_iter()
            .enumerate()
            .map(|(i, v)| {
                v.try_into().map_err(|_| {
                    crate::Error::InvalidParameter(i, "Failed to convert parameter".to_string())
                })
            })
            .collect(),
        None => Ok(Vec::new()),
    }
}

pub fn convert_named_params(
    named_params: Option<std::collections::HashMap<String, Value>>,
) -> Result<Vec<(String, libsql::Value)>, crate::Error> {
    match named_params {
        Some(map) => map
            .into_iter()
            .map(|(key, value)| {
                value.try_into().map(|v| (key.clone(), v)).map_err(|_| {
                    crate::Error::InvalidParameter(
                        0,
                        format!("Failed to convert named parameter '{}'", key),
                    )
                })
            })
            .collect(),
        None => Ok(Vec::new()),
    }
}

// Helper functions for vector operations
impl Value {
    /// Create a vector value from a JSON array string and vector type
    pub fn from_vector_json(json_str: &str, vector_type: VectorType) -> Result<Self, crate::Error> {
        let data: Vec<f32> = serde_json::from_str(json_str).map_err(|_| {
            crate::Error::InvalidParameter(0, "Invalid vector JSON format".to_string())
        })?;
        Ok(Value::Vector { data, vector_type })
    }

    /// Create a Float32 vector (most common type)
    pub fn vector32(data: Vec<f32>) -> Self {
        Value::Vector {
            data,
            vector_type: VectorType::Float32,
        }
    }

    /// Create a Float64 vector
    pub fn vector64(data: Vec<f32>) -> Self {
        Value::Vector {
            data,
            vector_type: VectorType::Float64,
        }
    }

    /// Get the SQL function call for this vector value
    pub fn to_vector_sql(&self) -> Result<String, crate::Error> {
        match self {
            Value::Vector { data, vector_type } => {
                let json_data = serde_json::to_string(data).map_err(|_| {
                    crate::Error::InvalidParameter(0, "Failed to serialize vector".to_string())
                })?;
                Ok(format!(
                    "{}('{}')",
                    vector_type.as_function_name(),
                    json_data
                ))
            }
            _ => Err(crate::Error::InvalidParameter(
                0,
                "Value is not a vector".to_string(),
            )),
        }
    }
}
